#ifndef __SCHEDULER_H__
#define __SCHEDULER_H__

/*
1.协程调度器有一个线程池，包含多个线程
2.每个线程可以有多个协程
3.协程可以在不同线程间切换
*/

#include "util.h"
#include "fiber.h"

#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory.h>
#include <memory>
#include <sys/types.h>
#include <thread>
#include <mutex>
#include <vector>
#include <string>
#include <list>
#include <atomic>

class Scheduler
{
public:
    using ptr = std::shared_ptr<Scheduler>;

    //线程池默认线程数量
    //是否使用当前调用线程(某线程执行协程调度器构造函数时，是否将这个线程自己放到协程调度器中)
    //协程调度器名称
    Scheduler(size_t threads = 1, bool use_caller = true, const std::string& name = "");
    virtual ~Scheduler();

    //返回当前协程调度器名称
    const std::string getName() const { return m_name; }

    //启动协程调度器
    void start();
    //停止协程调度器
    void stop();

    //返回当前协程调度器
    static Scheduler* GetThis();

    //返回当前协程调度器的调度协程
    static Fiber* GetMainFiber();

    //获取线程id
    static uint64_t GetThreadId(std::thread::id id);

    //调度任务
    //fc: 协程或者函数
    //thread：协程执行的线程id，-1表示任意线程
    template<class FiberorCb>
    void schedule(FiberorCb fc, uint64_t thread = 0)
    {
        bool need_tickle = false;
        {
            std::unique_lock<std::mutex> lock(m_mutex);
            need_tickle = scheduleNoLock(fc, thread);
        }
        if(need_tickle)
        {
            tickle();   
        }
    }

    //批量调度任务
    //任务指针数组 的起始迭代器和结束迭代器
    template<class InputIterator>
    void schedule(InputIterator begin, InputIterator end)
    {
        bool need_tickle = false;
        {
            std::unique_lock<std::mutex> lock(m_mutex);
            while(begin != end)
            {   
                //1.先解引用，解出指针
                //2.传递指针的地址
                need_tickle = scheduleNoLock(&(*begin), 0) || need_tickle;
                ++begin;
            }
        }
        if(need_tickle)
        {
            tickle();
        }
    }
protected:
    //通知协程调度器有任务了
    virtual void tickle();
        
    //返回是否可以停止
    virtual bool stopping();

    //协程无任务时可调度执行idle协程
    virtual void idle();

    //协程调度函数
    void run();

    //设置当前的协程调度器
    void setThis();

    //是否有空闲线程
    bool hasIdleThreads() { return m_idleThreadCount > 0; }

private:
    //添加调度任务 (无锁)
    //return true   当前任务队列为空，需要唤醒线程执行任务 
    //return false  任务队列中还有未处理的任务
    template<class FiberorCb>
    bool scheduleNoLock(FiberorCb fc, uint64_t thread)
    {
        bool need_tickle = m_fibers.empty();
        FiberAndThread ft(fc, thread);
        if(ft.fiber || ft.cb)
        {
            m_fibers.emplace_back(ft);
        }
        return need_tickle;
    }

private:
    //协程调度任务 - 任务可以是一个协程或者是一个回调函数
    struct FiberAndThread
    {
        Fiber::ptr fiber;         //协程
        std::function<void()> cb; //回调函数
        uint64_t threadId;        //线程id

        FiberAndThread():threadId(-1) {}

        FiberAndThread(Fiber::ptr f, uint64_t thr):fiber(f),threadId(thr) {}
        FiberAndThread(Fiber::ptr* f, uint64_t thr):threadId(thr)
        {
            fiber.swap(*f); //用swap交换到成员变量，让智能指针引用计数-1
        }

        FiberAndThread(std::function<void()> f, uint64_t thr):cb(f),threadId(thr) {}
        FiberAndThread(std::function<void()>* f, uint64_t thr):threadId(thr)
        {
            cb.swap(*f);
        }

        //重置状态
        void resetState()
        {
            fiber = nullptr;
            cb = nullptr;
            threadId = 0;
        }
    };

private:
    Fiber::ptr m_rootFiber; //协程调度器的调度协程
    std::string m_name;     //调度器名称
    std::mutex m_mutex;     //互斥量
    std::list<FiberAndThread> m_fibers;     //任务队列
    std::vector<std::shared_ptr<std::thread>> m_threads;    //线程池

protected:
    std::vector<uint64_t> m_threadIds; //协程下的线程id数组
    size_t m_threadCount = 0;     //线程数量
    std::atomic<size_t> m_activeThreadCount = {0};  //活跃线程数量
    std::atomic<size_t> m_idleThreadCount = {0};    //空闲线程数量
    bool m_stopping = true;       //当前是否停止
    bool m_autoStop = false;      //是否自动停止
    uint64_t m_rootThread = 0; //主线程id(use_caller)

};

#endif